package com.ruoyi.tcp.handler;


import com.ruoyi.tcp.entity.CustomerChannel;
import com.ruoyi.tcp.entity.beat.BeatEntity;
import com.ruoyi.tcp.entity.beat.ServerMessageEntity;
import com.ruoyi.tcp.entity.time.ServerTimeSyncMessageEntity;
import com.ruoyi.tcp.enums.TcpTypeStrEnum;
import com.ruoyi.tcp.messager.CustomerProtocol;
import com.ruoyi.tcp.service.ICardReaderService;
import com.ruoyi.tcp.util.Constants;
import com.ruoyi.tcp.util.TcpAesUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.Attribute;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;


/**
 * @author 蒋翔 SY100242
 * @date 2024/7/30 14:27
 * @email SY100242@hnlens.com
 */
//@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor
public class MessageHandler extends SimpleChannelInboundHandler<CustomerProtocol> {
    private Logger log = LoggerFactory.getLogger(MessageHandler.class.getName());

    @Autowired
    private ICardReaderService cardReaderService;


    private static final Map<Long, String> channelMap = new HashMap<>();

    static {
        channelMap.put(101L, "Z4T6U7RL08VJ5Y36");
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, CustomerProtocol message) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("\n");
            log.debug("channelId:{}", ctx.channel().id());
        }
        Long channelNo = message.getChannelNo();
        if (Objects.isNull(channelNo)) {
            return;
        }
        String password = channelMap.get(message.getChannelNo());
        if (Objects.isNull(password)) {
            if (log.isDebugEnabled()) {
                log.debug("未找到渠道密码:{}", message.getChannelNo());
            }
            return;
        }
        if (log.isDebugEnabled()) {
            log.debug("渠道号:" + message.getChannelNo());
            log.debug("收到消息:{}", TcpAesUtil.byte2hex(message.getEncryptData()));
        }
        if (!TcpAesUtil.checkCrc(TcpAesUtil.byte2hex(message.getEncryptData()).getBytes(StandardCharsets.UTF_8))) {
            log.debug("crc:校验失败");
            return;
        }
        byte[] finalGetData = TcpAesUtil.getNoChannelBytes(message.getEncryptData());
        log.info("除去渠道数据:{}", TcpAesUtil.byte2hex(finalGetData));
        byte[] finalGetDataNoCrc=new byte[finalGetData.length-2];
        System.arraycopy(finalGetData, 0, finalGetDataNoCrc, 0, finalGetData.length-2);
        byte[] decryptData = TcpAesUtil.decrypt(finalGetDataNoCrc, password);
        //去除补0的数据
        decryptData = TcpAesUtil.removeTrailingZeroBytes(decryptData);
        if (!TcpAesUtil.checkCrc(decryptData)) {
            log.debug("crc:校验失败");
            return;
        }

        String typeStr = TcpAesUtil.getTypeStr(decryptData);
        log.info("数据命令类型:{}", typeStr);

        if (TcpTypeStrEnum.BEAT.getValue().equalsIgnoreCase(typeStr)) {
            processBeat(ctx, decryptData, channelNo, password);
        } else if (TcpTypeStrEnum.DEVICE_CONNECTION.getValue().equalsIgnoreCase(typeStr)) {
            //RFID设备进入然后读取标签
            processDeviceIn(ctx, decryptData, channelNo, password);

        }else if(TcpTypeStrEnum.TIME_SYNC.getValue().equalsIgnoreCase(typeStr)) {
            processTimeSync(ctx, decryptData, channelNo, password);
        }

    }

    //处理心跳
    private  void processBeat(ChannelHandlerContext ctx, byte[] decryptData, Long channelNo, String password) {
        Long deviceNumber = TcpAesUtil.getDeviceNumber(decryptData);
        String deviceSerializeNumber = TcpAesUtil.getDeviceSerializeNumber(decryptData);
        int serializeNumber = TcpAesUtil.getSerializeNumber(decryptData);
        CustomerChannel customerChannel= createCustomerChannel(ctx, channelNo, deviceNumber, deviceSerializeNumber, password);

        LocalDateTime sendTime = TcpAesUtil.getSendTime(decryptData, 20, 28);
        //数据发生时间
        LocalDateTime occurTime = TcpAesUtil.getOccurTime(decryptData);
        //版本
        int version = TcpAesUtil.getVersion(decryptData);
        BeatEntity beatEntity = new BeatEntity();
        beatEntity.setDeviceNumber(deviceNumber);
        beatEntity.setDeviceSerializeNumber(deviceSerializeNumber);
        beatEntity.setSendTime(sendTime);
        beatEntity.setOccurTime(occurTime);
        beatEntity.setChannelNo(channelNo);
        beatEntity.setVersion(version);
        beatEntity.setChannelId(ctx.channel().id());
        InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIP = socketAddress.getAddress().getHostAddress();
        log.info("clientIP:{}",clientIP);
        cardReaderService.saveOrUpdateByDeviceNumber(beatEntity,clientIP);

        responseClient(TcpTypeStrEnum.RESPONSE.getValue(), ctx, channelNo, deviceNumber, serializeNumber , password,"");
    }

    //处理设备接入
    private void processDeviceIn(ChannelHandlerContext ctx, byte[] decryptData, Long channelNo, String password) {
        //发送版本
        int version = TcpAesUtil.getVersion(decryptData);
        log.info("设备接入-数据版本:{}", version);

        int len = TcpAesUtil.getDataLenth(decryptData);
        log.info("设备接入-数据长度:{}", len);

        int serializeNumber = TcpAesUtil.getSerializeNumber(decryptData);
        log.info("设备接入-数据序列号:{}", serializeNumber);

        LocalDateTime sendTime = TcpAesUtil.getSendTime(decryptData, 20, 28);
        log.info("设备接入-数据发送时间:{}", sendTime.format(DateTimeFormatter.ISO_DATE_TIME));

        LocalDateTime occurTime = TcpAesUtil.getOccurTime(decryptData);
        log.info("设备接入-数据发生时间:{}", occurTime.format(DateTimeFormatter.ISO_DATE_TIME));

        Long deviceNumber = TcpAesUtil.getDeviceNumber(decryptData);
        log.info("设备接入-设备编号:" + deviceNumber);

        String deviceSerializeNumber = TcpAesUtil.getDeviceSerializeNumber(decryptData);
        log.info("设备接入-设备序列号:" + deviceSerializeNumber);

        CustomerChannel customerChannel= createCustomerChannel(ctx, channelNo, deviceNumber, deviceSerializeNumber, password);


        responseClient(TcpTypeStrEnum.RESPONSE.getValue(), ctx, channelNo, deviceNumber, serializeNumber, password,"hello world");
    }

    //处理始终同步
    private void processTimeSync(ChannelHandlerContext ctx, byte[] decryptData, Long channelNo, String password) {
        Long deviceNumber = TcpAesUtil.getByteToLong(decryptData,20,36);
        log.info("时钟同步-设备编号:{}" , deviceNumber);
        String deviceSerializeNumber = TcpAesUtil.getByteToString(decryptData,36,64);
        log.info("时钟同步-设备序列号:{}" , deviceSerializeNumber);
        int serializeNumber = TcpAesUtil.getSerializeNumber(decryptData);
        log.info("时钟同步-数据序列号:{}" , serializeNumber);

        CustomerChannel customerChannel= createCustomerChannel(ctx, channelNo, deviceNumber, deviceSerializeNumber, password);
        ServerTimeSyncMessageEntity entity = new ServerTimeSyncMessageEntity();
        entity.setVersion(2);
        entity.setDeviceNumber(deviceNumber);
        entity.setSerializeNumber((long) serializeNumber);
        entity.setChannelNo(channelNo);
        entity.setTypeStr(TcpTypeStrEnum.TIME_SYNC.getValue());
        entity.setEncryptData(entity.createEncryptBytes(password));
        ctx.writeAndFlush(entity);
    }

    private static CustomerChannel createCustomerChannel(ChannelHandlerContext ctx, Long channelNo, Long deviceNumber, String deviceSerializeNumber, String password) {
        Attribute<CustomerChannel> attr = ctx.channel().attr(Constants.CUSTOMER_CHANNEL_KEY);
        if(Objects.isNull(attr.get())){
            CustomerChannel customerChannel = new CustomerChannel();
            customerChannel.setChannelNo(channelNo);
            customerChannel.setDeviceNumber(deviceNumber);
            customerChannel.setDeviceSerializeNumber(deviceSerializeNumber);
            customerChannel.setPassword(password);
            customerChannel.setSerialNumber(new AtomicLong(1L));
            customerChannel.setVersion(2);
            attr.set(customerChannel);
        }
        return attr.get();
    }

    private static void responseClient(String typeStr,ChannelHandlerContext ctx, Long channelNo, Long deviceNumber, long serializeNumber, String password,String msg) {
        ServerMessageEntity serverMessageEntity = new ServerMessageEntity();
        serverMessageEntity.setChannelNo(channelNo);
        serverMessageEntity.setDeviceNumber(deviceNumber);
        serverMessageEntity.setVersion(2);
        serverMessageEntity.setSerializeNumber(serializeNumber);
        serverMessageEntity.setTypeStr(typeStr);
        if(StringUtils.hasLength(msg)){
            serverMessageEntity.setMsg(msg);
        }


        serverMessageEntity.setEncryptData(serverMessageEntity.createEncryptBytes(password));
        ctx.writeAndFlush(serverMessageEntity);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        if (log.isDebugEnabled()) {
            log.debug("\n");
            log.debug("开始连接");
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("\n");
            log.debug("成功建立连接,channelId：{}", ctx.channel().id());
        }
        super.channelActive(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            switch (event.state()) {
                case READER_IDLE:
                    //表示设备没有在线 修改状态
                    cardReaderService.offline(ctx.channel().attr(Constants.CUSTOMER_CHANNEL_KEY).get().getDeviceSerializeNumber());
                    break;
                case ALL_IDLE:
                    log.info("超时");
                    cardReaderService.offline(ctx.channel().attr(Constants.CUSTOMER_CHANNEL_KEY).get().getDeviceSerializeNumber());
                    ctx.channel().close();
                    break;
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
}
